#ifdef _WIN32
#ifdef WITH_OPENGL

#include "renderer_opengl_win32.h"
#include "renderer_opengl.h"
#include "input_win32.h"

#include <windows.h>
#include <iostream>

using namespace krryn::system_imp;
using namespace krryn;

namespace{
	std::list<input_manager_win32*> g_InputManagers;

	LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
		switch (uMsg){
		case WM_CLOSE:
			PostQuitMessage(0);
			return 0;
		}

		for(std::list<input_manager_win32*>::iterator i = g_InputManagers.begin(); i != g_InputManagers.end(); i++){
			if((*i)->wnd_proc_accept(hWnd, uMsg)){
				return (*i)->wnd_proc_input(hWnd, uMsg, wParam, lParam);
			}
		}

		return DefWindowProc(hWnd, uMsg, wParam, lParam);
	}
}

renderer_opengl_win32::renderer_opengl_win32(renderer_opengl *a_Renderer, core::job_manager *a_Manager, const system::window_settings &a_Settings)
	: system::renderer(a_Manager), m_Settings(a_Settings), m_Renderer(a_Renderer), m_InputManager(0) {
	create_window(a_Settings);
	m_Renderer->setup();
}

renderer_opengl_win32::~renderer_opengl_win32(){
	m_Renderer->tear_down();
	destroy_window(m_Settings);
	delete m_Renderer;
}

system::input_manager *renderer_opengl_win32::get_input_manager(){
	if(m_InputManager == 0){
		m_InputManager = new input_manager_win32(m_hWnd);
		g_InputManagers.push_back(m_InputManager);
	}

	return m_InputManager;
}

void renderer_opengl_win32::begin_scene(){
	MSG l_Message;
	if (PeekMessage(&l_Message, NULL, 0, 0, PM_REMOVE)){
		if (l_Message.message == WM_QUIT){
			exit(0);
		}else{
			TranslateMessage(&l_Message);
			DispatchMessage(&l_Message);
		}
	}

	m_Renderer->begin_scene();
}

void renderer_opengl_win32::set_camera(view::camera *a_Camera){
	m_Renderer->set_camera(a_Camera);
}

void renderer_opengl_win32::set_projection_matrix(const math::matrix &a_ProjectionMatrix){
	m_Renderer->set_projection_matrix(a_ProjectionMatrix);
}

view::shader_catalog* renderer_opengl_win32::get_shader_catalog(){
	return m_Renderer->get_shader_catalog();
}

void renderer_opengl_win32::draw(scene::mesh *a_Mesh, const math::matrix &a_Transformation){
	m_Renderer->draw(a_Mesh, a_Transformation);
}

void renderer_opengl_win32::end_scene(){
	m_Renderer->end_scene();
	SwapBuffers(m_hDC);
}

void renderer_opengl_win32::create_window(const system::window_settings &a_Settings){
	GLuint l_PixelFormat;
	WNDCLASS l_WindowClass;
	DWORD l_ExtraStyle;
	DWORD l_Style;

	RECT l_WindowRect;
	l_WindowRect.left	= (long)0;
	l_WindowRect.right	= (long)a_Settings.m_Viewport.m_Width;
	l_WindowRect.top	= (long)0;
	l_WindowRect.bottom	= (long)a_Settings.m_Viewport.m_Height;

	m_hInstance					= GetModuleHandle(NULL);
	l_WindowClass.style			= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	l_WindowClass.lpfnWndProc	= (WNDPROC)WndProc;
	l_WindowClass.cbClsExtra	= 0;
	l_WindowClass.cbWndExtra	= 0;
	l_WindowClass.hInstance		= m_hInstance;
	l_WindowClass.hIcon			= LoadIcon(NULL, IDI_WINLOGO);
	l_WindowClass.hCursor		= LoadCursor(NULL, IDC_ARROW);
	l_WindowClass.hbrBackground	= NULL;
	l_WindowClass.lpszMenuName	= NULL;
	l_WindowClass.lpszClassName	= "krryn";

	if(!RegisterClass(&l_WindowClass)){
		std::clog << "Can't register window class" << std::endl;
	}
	
	if(a_Settings.m_Fullscreen){
		DEVMODE l_dmScreenSettings;
		memset(&l_dmScreenSettings, 0, sizeof(l_dmScreenSettings));
		l_dmScreenSettings.dmSize = sizeof(l_dmScreenSettings);
		l_dmScreenSettings.dmPelsWidth = a_Settings.m_Viewport.m_Width;
		l_dmScreenSettings.dmPelsHeight	= a_Settings.m_Viewport.m_Height;
		l_dmScreenSettings.dmBitsPerPel	= a_Settings.m_Viewport.m_BitsPerPixel;
		l_dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

		if(ChangeDisplaySettings(&l_dmScreenSettings, CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL){
			l_ExtraStyle = WS_EX_APPWINDOW;
			l_Style = WS_POPUP;
			ShowCursor(false);
		}else{
			std::clog << "Can't change to fullscreen mode" << std::endl;
		}
	}else{
		l_ExtraStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
		l_Style = WS_BORDER | WS_CAPTION | WS_SYSMENU;
	}

	AdjustWindowRectEx(&l_WindowRect, l_Style, FALSE, l_ExtraStyle);

	if (!(m_hWnd = CreateWindowEx(l_ExtraStyle, "krryn", a_Settings.m_Title.c_str(), l_Style | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
		0, 0, l_WindowRect.right - l_WindowRect.left, l_WindowRect.bottom - l_WindowRect.top, NULL, NULL, m_hInstance, NULL))){
		destroy_window(a_Settings);
		std::clog << "Failed to create window" << std::endl;
	}

	static	PIXELFORMATDESCRIPTOR l_Pfd = { sizeof(PIXELFORMATDESCRIPTOR), 1, PFD_DRAW_TO_WINDOW + PFD_SUPPORT_OPENGL + PFD_DOUBLEBUFFER, 
		PFD_TYPE_RGBA, a_Settings.m_Viewport.m_BitsPerPixel, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, PFD_MAIN_PLANE, 0, 0, 0, 0};
	
	if(!(m_hDC = GetDC(m_hWnd))){
		destroy_window(a_Settings);
		std::clog << "Can't create opengl context" << std::endl;
	}

	if(!(l_PixelFormat = ChoosePixelFormat(m_hDC, &l_Pfd))){
		destroy_window(a_Settings);
		std::clog << "No matching pixel format descriptor" << std::endl;
	}

	if(!SetPixelFormat(m_hDC, l_PixelFormat, &l_Pfd)){
		destroy_window(a_Settings);
		std::clog << "Can't set the pixel format" << std::endl;
	}

	if(!(m_hRC = wglCreateContext(m_hDC))){
		destroy_window(a_Settings);
		std::clog << "Can't create rendering context" << std::endl;
	}

	if(!wglMakeCurrent(m_hDC, m_hRC)){
		destroy_window(a_Settings);
		std::clog << "Can't activate rendering context" << std::endl;
	}

	ShowWindow(m_hWnd, SW_SHOW);
	SetForegroundWindow(m_hWnd);
	SetFocus(m_hWnd);
}

void renderer_opengl_win32::destroy_window(const system::window_settings &a_Settings){
	if(a_Settings.m_Fullscreen){
		ChangeDisplaySettings(NULL, 0);
		ShowCursor(TRUE);
	}

	if(m_hRC){
		wglMakeCurrent(NULL, NULL);
		wglDeleteContext(m_hRC);
		m_hRC = NULL;
	}

	if(m_hDC && !DeleteDC(m_hDC)){
		m_hDC = NULL;
	}

	if(m_hWnd && !DestroyWindow(m_hWnd)){
		m_hWnd = NULL;
	}

	if(!UnregisterClass("krryn", m_hInstance)){
		m_hInstance = NULL;
	}
}


#endif
#endif